﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using MQTTServer.Model;
using MQTTServer.Model.UDPEvents;
using Newtonsoft.Json;
using System.Windows.Forms;

namespace MQTTServer.Bll
{
    public class UPDServerBll : UPDServerBase
    {


        public UPDServerBll()
        {

        }
        /// <summary>
        /// 服务器启动
        /// </summary>
        public void ServerStart()
        {
            try
            {
                const uint IOC_IN = 0x80000000;
                int IOC_VENDOR = 0x18000000;
                int SIO_UDP_CONNRESET = (int)(IOC_IN | IOC_VENDOR | 12);
                //ServerIpPoint = new IPEndPoint(IPAddress.Any, ServerPort);
                ServerIpPoint = new IPEndPoint(IPAddress.Parse(ServerIp), ServerPort);
                updServer = new UdpClient();
                // updServer = new UdpClient(ServerIpPoint);
                updServer.Client.Bind(ServerIpPoint);
                //windows的一个bug，设备为UDP低级别操作模式
                updServer.Client.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
                //updServer_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                //updServer_Socket.Bind(ServerIpPoint);

                string str = string.Format("UDP服务器启动成功！Ip:{0},端口{1}。", ServerIpPoint.Address, ServerIpPoint.Port.ToString());
                //开始异步接收
                BeginReceive(null);
                OnServerConnectd(ServerIpPoint, str);
            }
            catch (Exception e)
            {
                string str = string.Format("UDP服务器启动失败！Ip:{0},端口{1}。原因{2}。", ServerIpPoint.Address, ServerIpPoint.Port.ToString(), e.Message);
                OnServerConnectd(ServerIpPoint, str);
            }

        }
        public void ServerClose()
        {
            try
            {
                updServer.Client.Shutdown(SocketShutdown.Both);
                updServer.Client.Close(1000);//n是秒数
                updServer.Close();
                //updServer_Socket.Shutdown(SocketShutdown.Both);
                //updServer_Socket.Close(1000);//n是秒数
                //updServer_Socket.Close();
                string str = string.Format("UDP服务器关闭成功！Ip:{0},端口{1}。", ServerIpPoint.Address, ServerIpPoint.Port.ToString());
                updServer = null;
                //updServer_Socket = null;
                ServerIpPoint = null;
                OnServerConnectd(ServerIpPoint, str);
            }
            catch (Exception e)
            {
                string str = string.Format("UDP服务器关闭失败！Ip:{0},端口{1}。原因{2}。", ServerIpPoint.Address, ServerIpPoint.Port.ToString(), e.Message);
                OnServerConnectd(ServerIpPoint, str);
            }

        }
        /// <summary>
        /// 异步接收方法
        /// </summary>
        private void BeginReceive(IPEndPoint tmpIp)
        {
            try
            {
                //在本机指定的端口接收
                UdpState udpState = new UdpState();
                udpState.IpEndPoint = null;
                udpState.udpClient = updServer;
                //接收从远程主机发送过来的信息；
                IAsyncResult ar = updServer.BeginReceive(ReceiveUdpClientCallback, udpState);
                //IAsyncResult ar = updServer_Socket.BeginReceive(ReceiveUdpClientCallback, udpState,);
                // ar.AsyncWaitHandle.WaitOne();
            }
            catch (System.Net.Sockets.SocketException e1)
            {
                string str = "接收数据完成回调失败,远程主机强迫关闭了一个现有的连接。";
                this.OnServerExceptiond(str, e1);
                //  if (tmpIp!= null)
                BeginReceive(tmpIp);//继续接受数据
            }
            catch (Exception ex)
            {
                BeginReceive(tmpIp);//继续接受数据
                string str = "开始接收数据失败";
                this.OnServerExceptiond(str, ex);
            }
        }
        /// <summary>
        /// 接收信息回调方法
        /// </summary>
        /// <param name="ar"></param>
        void ReceiveUdpClientCallback(IAsyncResult ar)
        {
            IPEndPoint tmpIp = null;
            try
            {
                //获得服务端UDP对象
                UdpClient ServerUDP = (UdpClient)((UdpState)(ar.AsyncState)).udpClient;
                if (ServerUDP.Client != null)
                {
                    //通过服务端UDP对象接收数据并通过按引用传递的方式，获得当前连接的客户端信息。
                    ClientIpPoint = (IPEndPoint)((UdpState)(ar.AsyncState)).IpEndPoint;
                    Byte[] receiveBytes = ServerUDP.EndReceive(ar, ref ClientIpPoint);
                    tmpIp = ClientIpPoint;
                    string strdata = Encoding.UTF8.GetString(receiveBytes, 0, receiveBytes.Length);

                    ClientPayloadDataModel cdata = new ClientPayloadDataModel();
                    cdata = JsonConvert.DeserializeObject<ClientPayloadDataModel>(strdata);
                    cdata.LastReceiveDataTime = DateTime.Now;
                    cdata.ClinetIpProt = ClientIpPoint.ToString();

                    if (AddClientList(cdata))//客户端第一次登陆
                    {

                    }
                    else//正常数据接收
                    {
                        //将此客户最后收数据的时间在客户集合中更新
                        Server_ClientListModel.UDPClientList[cdata.SendDataClientName] = cdata;
                        this.OnServerReceived(strdata);
                    }
                    BeginReceive(tmpIp);//继续接受数据
                }
            }
            catch (System.Net.Sockets.SocketException e1)
            {
                string str = "接收数据完成回调失败,远程主机强迫关闭了一个现有的连接。";
                // if(tmpIp!=null)
                BeginReceive(tmpIp);//继续接受数据
                this.OnServerExceptiond(str, e1);
            }
            catch (Exception ex)
            {
                // BeginReceive();//继续接受数据
                BeginReceive(tmpIp);//继续接受数据
                string str = "接收数据完成回调失败";
                this.OnServerExceptiond(str, ex);
            }
        }
        /// <summary>
        /// 将单个用户纯字符串数据转换为JSON串数据
        /// </summary>
        /// <param name="username"></param>
        /// <param name="data"></param>
        /// <param name="siliao"></param>
        /// <returns></returns>
        public string GetOneCientJSONDataToText(string username, string data, bool siliao)
        {
            ClientPayloadDataModel cdata = new ClientPayloadDataModel();
            cdata.SendDataClientName = "服务器";
            cdata.ClientName = username.Length == 0 ? "所有人" : username;
            cdata.DataMessage = data;
            cdata.DataMessageTime = DateTime.Now.ToString();
            cdata.IsSiLiao = siliao;
            string JsonStr = JsonConvert.SerializeObject(cdata);
            return JsonStr;
        }
        /// <summary>
        /// 向所有用户发送纯字符串数据 （此方法会转换为JSON串数据）
        /// </summary>
        /// <param name="data"></param>
        /// <param name="siliao"></param>
        public void SendAllClientJsonTextFromText(string strData, bool siliao)
        {
            foreach (ClientPayloadDataModel c in Server_ClientListModel.UDPClientList.Values)
            {
                IPEndPoint ipPoint = GetIPEndPointByStr(c.ClinetIpProt);
                string str = GetOneCientJSONDataToText(c.SendDataClientName, strData, siliao);
                BeginSendData(str, ipPoint);
            }

        }
        /// <summary>
        /// 给所有客户端发送数据  发送的数据为JSON串
        /// </summary>
        /// <param name="str"></param>
        public void SendAllClient(string jsonData)
        {
            foreach (ClientPayloadDataModel c in Server_ClientListModel.UDPClientList.Values)
            {
                IPEndPoint ipPoint = GetIPEndPointByStr(c.ClinetIpProt);
                BeginSendData(jsonData, ipPoint);
            }
        }
        /// <summary>
        /// 开始发送数据根据用户名  发送的数据为JSON串
        /// </summary>
        /// <param name="str"></param>
        /// <param name="username"></param>
        public void BeginSendData(string str, string username)
        {
            //var result = from cobj in Server_ClientListModel.ClientList where cobj.SendDataClientName.Equals(username) select cobj;
            //if (result.Count() > 0)
            //    BeginSendData(str, result.ElementAt(0).ClinetIpProt);//通过UDPServer连接到当前的客户端发送对象，准备向当前客户端发送数据
            if (Server_ClientListModel.UDPClientList.Keys.Contains(username))
            {
                ClientPayloadDataModel client = Server_ClientListModel.UDPClientList[username];
                IPEndPoint ipPoint = GetIPEndPointByStr(client.ClinetIpProt);
                BeginSendData(str, ipPoint);
            }
        }
        /// <summary>
        ///开始发送数据根据用户IP  发送的数据为JSON串
        /// </summary>
        /// <param name="str"></param>
        public void BeginSendData(string strData, IPEndPoint clientIp)
        {
            try
            {
                //if (!updServer.Client.Poll(500, SelectMode.SelectRead))
                //{
                //这句一定要去掉，服务器端不可Connect客户端IP ，否则会绑定客户端，只能一对一通讯了。  
                //updServer.Connect(clientIp);//通过UDPServer连接到当前的客户端发送对象，准备向当前客户端发送数据
                UdpState SendudpState = new UdpState();
                SendudpState.IpEndPoint = clientIp;
                Byte[] sendBytes = Encoding.UTF8.GetBytes(strData);
                updServer.BeginSend(sendBytes, sendBytes.Length, clientIp, new AsyncCallback(SendCallback), SendudpState);
                this.OnServerSendDatad(strData);
                //}
                //else
                //{
                //    string str = "掉线";
                //}
            }
            catch (InvalidOperationException e1)
            {
                //断开套接字连接后，只能通过异步方式再次重新连接，而且只能连接到不同的 EndPoint。
                string str = "断开套接字连接后，只能通过异步方式再次重新连接，而且只能连接到不同的 EndPoint。";
                //updServer.Client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                this.OnServerExceptiond("开始发送数据失败", e1);

            }
            catch (Exception ex)
            {
                //IEnumerable<ClientPayloadDataModel> result = from cobj in Server_ClientListModel.ClientList where cobj.ClinetIpProt.Equals(clientIp.ToString()) select cobj;
                //List<ClientPayloadDataModel> clientlist = new List<ClientPayloadDataModel>(result);
                //Server_ClientListModel.ClientList.Remove(clientlist[0]);
                this.OnServerExceptiond("开始发送数据失败", ex);
            }
        }
        // 发送完毕后的回调函数
        private void SendCallback(IAsyncResult iar)
        {
            try
            {
                UdpState udpState = iar.AsyncState as UdpState;
                string ip = udpState.IpEndPoint.ToString();
            }
            catch (Exception ex)
            {
                string str1 = "发送数据完成回调失败";
                this.OnServerExceptiond(str1, ex);
            }
        }

        /// <summary>
        /// 根据连接的客户IP信息，添加到服务器的客户端集合中
        /// </summary>
        /// <param name="ClientIpPoint"></param>
        private bool AddClientList(ClientPayloadDataModel ClientObj)
        {
            ///如果用户集合中，没有对应的IP信息用户，则此用户为第一次登陆
            if (!Server_ClientListModel.UDPClientList.Keys.Contains(ClientObj.SendDataClientName))//此客户第一次登陆
            {
                AddCilentTolist(ClientObj);
                return true;
            }
            else
            {
                ClientPayloadDataModel client = Server_ClientListModel.UDPClientList[ClientObj.SendDataClientName];
                //如果同一个用户登录的IP 端口不一致，则重新添加用户，挤掉旧客户
                if (!client.ClinetIpProt.Equals(ClientObj.ClinetIpProt))
                {
                    AddCilentTolist(ClientObj);
                    return true;
                }
            }

            return false;
        }
        private void AddCilentTolist(ClientPayloadDataModel ClientObj)
        {
            //将此客户信息添加到客户列表
            // Server_ClientListModel.UDPClientList.Add(ClientObj.SendDataClientName, ClientObj);
            Server_ClientListModel.UDPClientList[ClientObj.SendDataClientName] = ClientObj;
            //构造发送数据
            ClientPayloadDataModel clinet = new ClientPayloadDataModel();
            clinet.ClinetIpProt = ClientObj.ClinetIpProt;
            clinet.ClientDataType = 1;
            clinet.ClientList.Add(ClientObj);
            string oneclient = JsonConvert.SerializeObject(clinet);
            //给所有客户发送此单个客户登陆信息
            SendAllClient(oneclient);
            clinet.ClientList.Clear();
            //给第一次登陆的客户发送所有在线客户列表
            clinet.ClientList.AddRange(Server_ClientListModel.UDPClientList.Values);
            string allclient = JsonConvert.SerializeObject(clinet);
            BeginSendData(allclient, GetIPEndPointByStr(ClientObj.ClinetIpProt));

            OnNewClientConnectd(ClientObj);
        }

        private IPEndPoint GetIPEndPointByStr(string ipPort)
        {
            int tmp = ipPort.IndexOf(":");
            IPAddress ip = IPAddress.Parse(ipPort.Substring(0, tmp));
            int prot = int.Parse(ipPort.Substring(tmp + 1));
            IPEndPoint ipPoint = new IPEndPoint(ip, prot);
            return ipPoint;
        }
        private ClientPayloadDataModel GetClientData(IPEndPoint IPEndPoint, string strdata)
        {
            ClientPayloadDataModel clientData = new ClientPayloadDataModel();


            return clientData;

        }
        /// <summary>
        /// 清除掉线客户
        /// </summary>
        public void ClientClose()
        {
            if (this != null)
            {
                //准备删除的客户临时装载变量
                List<ClientPayloadDataModel> cnameList = new List<ClientPayloadDataModel>();
                int count = Server_ClientListModel.UDPClientList.Count;
                //所有客户
                ClientPayloadDataModel[] clist = new ClientPayloadDataModel[count];
                //拷贝在线客户列表单独使用  防止生产环境的客户列表死锁
                Server_ClientListModel.UDPClientList.Values.CopyTo(clist, 0);
                foreach (ClientPayloadDataModel c in clist)
                {
                    TimeSpan ts1 = new TimeSpan(c.LastReceiveDataTime.Ticks);
                    TimeSpan ts2 = new TimeSpan(DateTime.Now.Ticks);
                    TimeSpan ts = ts1.Subtract(ts2).Duration();
                    if (ts.Minutes > 2)//超过过两分钟没收到数据时
                    {
                        cnameList.Add(c);
                    }
                }
                for (int i = 0; i < cnameList.Count; i++)
                {
                    //移除超时客户
                    Server_ClientListModel.UDPClientList.Remove(cnameList[i].SendDataClientName);
                }
                //构造发送数据
                ClientPayloadDataModel clinet1 = new ClientPayloadDataModel();
                clinet1.ClientDataType = 2;
                //给第一次登陆的客户发送所有在线客户列表
                clinet1.ClientList.AddRange(cnameList);
                string colse_all_client = JsonConvert.SerializeObject(clinet1);
                //给所有客户发送此单个客户登陆信息
                SendAllClient(colse_all_client);
                //触发事件  更新界面
                OnCloseClientConnectd(clinet1);
            }
        }
    }
}
